using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Net;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using PacketScreensaver.Net.PacketMonitor;

namespace PacketScreensaver.Windows.Screensaver
{
    public partial class TestForm : Form
    {
        Device _device;
        Material _material;        
        Timer _timer;
        PresentParameters _presentParameters;
        List<IEntity> _packetEntities;
        List<IEntity> _arrivedPackets;
        List<Monitor> _monitors;
        Dictionary<IPAddress, Vector3> _hosts;
        Random _random;
        
        float _farPlane = 300f;
        int _maxViewablePackets = 350;

        public TestForm()
        {
            this.Size = new Size(500, 500);
            OneTimeInit();
            InitializePacketMonitor();
            InitializeDirect3D();
            StartRefreshCycle();
        }

        void InitializePacketMonitor()
        {
            _random = new Random();
            _packetEntities = new List<IEntity>();
            _arrivedPackets = new List<IEntity>();
            _hosts = new Dictionary<IPAddress, Vector3>();

            IPAddress[] hosts = Dns.GetHostEntry(Dns.GetHostName()).AddressList;

            _monitors = new List<Monitor>();
            foreach (IPAddress host in hosts)
            {                
                Monitor m = new Monitor(host);
                m.NewPacket += new NewPacketEventHandler(this.HandlePacket);
                m.Start();
                _monitors.Add(m);
            }
        }

        void StartPacketMonitor()
        {
            foreach (Monitor m in _monitors)
                if (!m.IsRunning)
                    m.Start();
        }

        void StopPacketMonitor()
        {
            foreach (Monitor m in _monitors)
                if (m.IsRunning)
                    m.Stop();
        }

        void HandlePacket(Monitor pm, Packet p)
        {
            if (_packetEntities.Count < _maxViewablePackets)
            {
                if (!_hosts.ContainsKey(p.SourceAddress))
                    _hosts.Add(p.SourceAddress, new Vector3(_random.Next(-80, 80), _random.Next(-80, 80), 301));

                if (!_hosts.ContainsKey(p.DestinationAddress))
                    _hosts.Add(p.DestinationAddress, new Vector3(_random.Next(-80, 80), _random.Next(-80, 80), 301));

                Vector3 initialPosition = new Vector3();
                Vector3 destination = new Vector3();
                int speed = 4;

                // incomming
                if (p.DestinationAddress.ToString().Equals(pm.IP.ToString()))
                {
                    initialPosition = _hosts[p.SourceAddress];
                    destination = new Vector3(0, 10, 0);
                    speed = 0 - speed;
                }
                else
                {
                    initialPosition = new Vector3(0, -5, 0);
                    destination = _hosts[p.DestinationAddress];
                }

                lock (_packetEntities)
                    _packetEntities.Add(new Entity(_device, initialPosition, destination, speed));
            }
        }

        void StartRefreshCycle()
        {
            int timerInterval = 30; //milliseconds

            _timer = new Timer();
            _timer.Enabled = true;
            _timer.Interval = timerInterval;
            _timer.Tick += new EventHandler(FrameMove);
            _timer.Start();
        }

        void OneTimeInit()
        {
            AdapterInformation adapterInfo = Manager.Adapters[0];
            _presentParameters = new PresentParameters();
            _presentParameters.Windowed = true;
            _presentParameters.SwapEffect = SwapEffect.Discard;
            _presentParameters.BackBufferFormat = adapterInfo.CurrentDisplayMode.Format;
            _presentParameters.EnableAutoDepthStencil = true;
            _presentParameters.AutoDepthStencilFormat = DepthFormat.D16;

            // Check for hardware device shader support
            Caps hardware = Manager.GetDeviceCaps(adapterInfo.Adapter, DeviceType.Hardware);

            if((hardware.VertexShaderVersion >= new Version(2, 0)) &&
					(hardware.PixelShaderVersion >= new Version(2, 0))) 
            {
				// Default to using software processing
				CreateFlags flags = CreateFlags.SoftwareVertexProcessing;

				// Use hardware if it is available
				if(hardware.DeviceCaps.SupportsHardwareTransformAndLight)
					flags = CreateFlags.HardwareVertexProcessing;

				// Use pure hardware if we can
				if(hardware.DeviceCaps.SupportsPureDevice)
					flags |= CreateFlags.PureDevice;

				// Create the device with hardware shaders
                _device = new Device(
                    adapterInfo.Adapter, 
                    DeviceType.Hardware,
                    this, 
                    flags,
                    _presentParameters
				);
			} 
            else 
            {
				// No hardware shader support so use reference device
                _device = new Device(
                    adapterInfo.Adapter, 
                    DeviceType.Reference,
                    this, 
					CreateFlags.SoftwareVertexProcessing,
                    _presentParameters
				);
            }
            
            _device.DeviceReset += new EventHandler(OnDeviceReset);
        }

        void InitializeDirect3D()
        {
            CreateMaterials();
            CreateLights();
            InitializeView();
        }

        void OnDeviceReset(object o, EventArgs e)
        {
            InitializeDirect3D();
        }

        void CreateMaterials()
        {
            _material = new Material();
            _material.Diffuse = Color.FromArgb(1, 255, 210, 0);
        }

        void CreateLights()
        {
            Light light0 = _device.Lights[0];
            light0.Type = LightType.Point;
            light0.Position = new Vector3(0, 10, -10);
            light0.Diffuse = Color.FromArgb(0, 255, 255, 255);
            light0.Attenuation0 = 1;
            light0.Range = _farPlane;
            light0.Enabled = true;
            light0.Update();

            _device.RenderState.Lighting = true;
            //_device.RenderState.Ambient = Color.FromArgb(0, 200, 200, 200);
        }

        void InitializeView()
        {
            Vector3 eyePosition = new Vector3(0, 0, -10);
            Vector3 direction = new Vector3(0, 0, 0);
            Vector3 upDirection = new Vector3(0, 1, 0);

            Matrix viewMatrix = Matrix.LookAtLH(eyePosition, direction, upDirection);

            _device.SetTransform(TransformType.View, viewMatrix);

            float fieldOfView = (float)Math.PI / 4;
            float aspectRatio = 1.0f;
            float nearPlane = 1.0f;

            Matrix projectionMatrix = Matrix.PerspectiveFovLH(fieldOfView, aspectRatio, nearPlane, _farPlane);
            _device.SetTransform(TransformType.Projection, projectionMatrix);
        }

        void FrameMove(object sender, EventArgs e)
        {
            _device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black.ToArgb(), 1.0f, 0);
            _device.BeginScene();
            _device.Material = _material;

            Matrix world = new Matrix();

            lock (_packetEntities)
            {
                foreach (IEntity packet in _packetEntities)
                {
                    Matrix position = Matrix.Translation(packet.CurrentPosition);
                    _device.SetTransform(TransformType.World, position);
                    packet.Sphere.DrawSubset(0);
                    packet.CalculateNextPosition();

                    // has the packet arrived at its destination?
                    if (packet.HasArrived)
                        _arrivedPackets.Add(packet);
                }
            }

            // remove any packets that have arrived at their destination
            IEntity arrivedPacket = null;
            for (int i = _arrivedPackets.Count - 1; i >= 0; i--)
            {
                lock (_packetEntities)
                {
                    arrivedPacket = _packetEntities[i];
                    _packetEntities.Remove(arrivedPacket);
                    _arrivedPackets.Remove(arrivedPacket);
                    arrivedPacket = null;
                }
            }

            _device.EndScene();
            _device.Present();
        }
    }
}